package songgao.papers.iEC;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

@SuppressWarnings("serial")
public class ClientAJAXHandler extends HttpServlet {
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		String action = req.getParameter("action");
		if (action == null)
			resp.getWriter().println("action parameter required");
		else if (action.toLowerCase().equals("register")) {
			this.registerSlave(req, resp);
		}
	}

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		String action = req.getParameter("action");
		if (action == null)
			resp.getWriter().println("action parameter required");
		else if (action.toLowerCase().equals("getpopulation")) {
			this.getPopulation(req, resp);
		} else if (action.toLowerCase().equals("submitpopulation")) {
			this.submitPopulation(req, resp);
		}
	}

	public void submitPopulation(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		String slaveKey;
		int generation;
		String preKey;
		ArrayList<Individual> individuals = new ArrayList<Individual>();

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			String xmlString = req.getParameter("xml");
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(xmlString));
			Document doc = docBuilder.parse(is);

			Element docElement = doc.getDocumentElement();// .getFirstChild();
			slaveKey = docElement.getAttribute("SlaveKey");
			if (slaveKey == null)
				throw new Exception();

			Node popNode = docElement.getFirstChild();
			generation = Integer.parseInt(popNode.getAttributes().getNamedItem("Generation").getNodeValue());
			preKey = popNode.getAttributes().getNamedItem("PredecessorKey").getNodeValue();
			for (int i = 0; i < popNode.getChildNodes().getLength(); i++) {
				Node indNode = popNode.getChildNodes().item(i);
				Individual individual = new Individual();
				individual.setFitness(Double.parseDouble(indNode.getAttributes().getNamedItem("Fitness").getNodeValue()));
				ArrayList<Integer> geneArrayList = new ArrayList<Integer>();
				for (int j = 0; j < indNode.getChildNodes().getLength(); j++)
					geneArrayList.add(Integer.parseInt(indNode.getChildNodes().item(j).getAttributes().getNamedItem("Value").getNodeValue()));
				individual.setGene(geneArrayList);
				individuals.add(individual);
			}
		} catch (Exception ex) {
			resp.getWriter().println("error: broken xml");
			return;
		}

		

		try {
			Boolean result = Controller.INSTANCE.submitPopulation(slaveKey, generation, individuals, preKey);
			resp.setContentType("text/xml");
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			Document doc = db.newDocument();
			Element resultElement = doc.createElement("SubmitPopulationResult");
			resultElement.setAttribute("Result", result ? "success" : "failed");
			doc.appendChild(resultElement);

			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();

			trans.setOutputProperty(OutputKeys.INDENT, "yes");

			StringWriter sw = new StringWriter();
			DOMSource source = new DOMSource(doc);
			trans.transform(source, new StreamResult(sw));

			resp.getWriter().println(sw.toString());
		} catch (Exception ex) {
			resp.getWriter().println(ex);
		}
	}

	public void getPopulation(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		String slaveKey;

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			String xmlString = req.getParameter("xml");
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(xmlString));
			Document doc = docBuilder.parse(is);
			Element docElement = doc.getDocumentElement();// .getFirstChild();
			slaveKey = docElement.getAttribute("SlaveKey");
			if (slaveKey == null)
				throw new Exception();
		} catch (Exception ex) {
			resp.getWriter().println("error: broken xml");
			return;
		}

		Population pop = Controller.INSTANCE.getPopulation(slaveKey);
		if (pop == null) {
			resp.getWriter().println("error: wrong slaveKey");
			return;
		}
		ArrayList<Individual> inds = new ArrayList<Individual>();
		for (String ind : pop.getIndividualKeys())
			inds.add(DataManager.INSTANCE.getIndividual(ind));

		Parameters parameter = DataManager.INSTANCE.getParameters();
		//TODO: read parameter
		try {

			resp.setContentType("text/xml");
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();

			Document doc = db.newDocument();
			Element root = doc.createElement("GetPopulationResult");
			Element parameterElement = doc.createElement("Parameter");

			root.appendChild(parameterElement);

			Element populationElement = doc.createElement("Population");
			populationElement.setAttribute("Key", pop.getEncodedKey());
			populationElement.setAttribute("Generation", String.valueOf(pop.getGeneration()));

			for (Individual ind : inds) {
				Element individualElement = doc.createElement("Individual");
				individualElement.setAttribute("Fitness", String.valueOf(ind.getFitness()));
				for (int i = 0; i < ind.getGene().size(); i++) {
					Element geneElement = doc.createElement("Gene");
					geneElement.setAttribute("Value", String.valueOf(ind.getGene().get(i)));
					individualElement.appendChild(geneElement);
				}
				populationElement.appendChild(individualElement);
			}

			root.appendChild(populationElement);

			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();

			trans.setOutputProperty(OutputKeys.INDENT, "yes");

			doc.appendChild(root);
			StringWriter sw = new StringWriter();
			DOMSource source = new DOMSource(doc);
			trans.transform(source, new StreamResult(sw));

			resp.getWriter().println(sw.toString());
		} catch (Exception ex) {
			resp.getWriter().println(ex);
		}
	}

	public void registerSlave(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		String slaveKey = Controller.INSTANCE.registerNewSlave();
		try {

			resp.setContentType("text/xml");
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			Document doc = db.newDocument();
			Element slaveKeyElement = doc.createElement("RegistrationResult");
			slaveKeyElement.setAttribute("SlaveKey", slaveKey);
			doc.appendChild(slaveKeyElement);

			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();

			trans.setOutputProperty(OutputKeys.INDENT, "yes");

			StringWriter sw = new StringWriter();
			DOMSource source = new DOMSource(doc);
			trans.transform(source, new StreamResult(sw));

			resp.getWriter().println(sw.toString());
		} catch (Exception ex) {
			resp.getWriter().println(ex);
		}
	}
}
